home *** CD-ROM | disk | FTP | other *** search
-
-
-
-
-
-
- Chapter 4
- FUNCTIONS
-
- This chapter discusses enhancements in the capabilities of
- functions that have been made to C++. These changes make
- programming more convenient and permit the compiler to do further
- checking for errors. A fair amount of time is also spent in this
- chapter teaching the modern form of function definition and
- prototyping.
-
- Prototyping allows the compiler to do additional type checking for
- your function calls which can detect some programming errors. The
- first two example programs in this chapter are designed to teach
- prototyping and what it will do for you. Prototyping is a
- relatively new addition to C, so even some experienced C
- programmers are not familiar with it. If you have experience with
- prototyping you can skip directly to the section named PASS BY
- REFERENCE on page 4-4 of this chapter.
-
-
- PROTOTYPES
- _________________________________________________________________
-
- Examine the file named PROTYPE1.CPP for our ================
- first look at a prototype and an illustration of PROTYPE1.CPP
- how it is used. The prototyping used in C++ is ================
- no different than that used in ANSI-C.
- Actually, many C programmers take a rather dim
- view of prototyping and seem reluctant to use it, but with C++ it
- is considerably more important and is in much heavier use. In
- fact, prototyping is required to be used in some situations in C++.
-
- A prototype is a limited model of a more complete entity to come
- later. In this case, the full function is the complete entity to
- come later and the prototype is illustrated in line 4. The
- prototype gives a model of the interface to the function that can
- be used to check the calls to the function for the proper number
- of parameters and the correct types of parameters. Each call to
- the function named do_stuff() must have exactly three parameters
- or the compiler will give an error message. In addition to the
- correct number of parameters, the types must be compatible or the
- compiler will issue an error message. Notice that when the
- compiler is working on lines 12 and 13, the type checking can be
- done based on the prototype in line 4 even though the function
- itself is not yet defined. If the prototype is not given, the
- number of parameters will not be checked, nor will the types of the
- parameters be checked. Even if you have the wrong number of
- parameters, you will get an apparently good compile and link, but
- the program may do some very strange things when it is executed.
-
-
- Page 4-1
-
- Chapter 4 - Functions
-
-
- To write the prototype, simply copy the header from the function
- to the beginning of the program and append a semicolon to the end
- as a signal to the compiler that this is not a function but a
- prototype. The variable names given in the prototype are optional
- and act merely as comments to the program reader since they are
- completely ignored by the compiler. You could replace the variable
- name wings in line 4 with your first name and there would be no
- difference in compilation. Of course, the next person that had to
- read your program would be somewhat baffled with your choice of
- variable names.
-
- In this case, the two function calls to this function, given in
- lines 12 and 13, are correct so no error will be listed during
- compilation.
-
- Even though we wish to use the char type for eyes in the function,
- we wish to use it as a number rather than as a character. The cast
- to int in line 20 is required to force the printout of the
- numerical value rather than an ASCII character. The next example
- program is similar but without the cast to int.
-
-
-
- COMPATIBLE TYPES
- _________________________________________________________________
-
- We mentioned compatible types earlier so we should review them just
- a bit in order to make our discussion of prototyping complete.
- Compatible types are any simple types that can be converted from
- one to another in a meaningful way. For example, if you used an
- integer as the actual parameter and the function was expecting a
- float type as the formal parameter, the system would do the
- conversion automatically, without mentioning it to you. This is
- also true of a float changing to a char, or a char changing to an
- int. There are definite conversion rules which would be followed.
- These rules are given in great detail in section 3.2 of the draft
- of the ANSI-C standard and are also given on page 198 of the second
- edition of the K&R reference.
-
- If we supplied a pointer to an integer as the actual parameter and
- expected an integer as the formal parameter in the function, the
- conversion would not be made because they are two entirely
- different kinds of values. Likewise, a structure would not be
- converted automatically to a long float, an array, or even to a
- different kind of structure, they are all incompatible and cannot
- be converted in any meaningful manner. The entire issue of type
- compatibility as discussed in chapter 2 of this tutorial applies
- equally well to the compatibility of types when calling a function.
- Likewise, the type specified as the return type, in this case void,
- must be compatible with the expected return type in the calling
- statement, or the compiler will issue a warning.
-
-
-
- Page 4-2
-
- Chapter 2 - Functions
-
-
- HOW DOES PROTOTYPING WORK?
- _________________________________________________________________
-
- This is your chance to try prototyping for yourself and see how
- well it works and what kinds of error messages you get when you do
- certain wrong things. Change the actual parameters in line 12 to
- read (12.2, 13, 12345) and see what the compiler says about that
- change. It will probably say nothing because they are all type
- compatible. If you change it to read (12.0, 13), it will issue a
- warning or error because there are not enough arguments given.
- Likewise you should receive an error message if you change one of
- the parameters in line 13 to an address by putting an ampersand in
- front of one of the variable names. Finally, change the first word
- in line 4 from void to int and see what kind of error message is
- given. You will first be required to make the function header in
- line 16 agree with the prototype, then you will find that there is
- not a variable returned from the function. You should have a good
- feeling that prototyping is doing something good for you after
- making these changes.
-
- Be sure to compile and execute this program then make the changes
- recommended above, attempting to compile it after each change.
-
-
-
- A LITTLE MORE PROTOTYPING
- _________________________________________________________________
-
- Examine the next example program named ================
- PROTYPE2.CPP for a little more information on PROTYPE2.CPP
- prototyping. This program is identical to the ================
- last one except for a few small changes. The
- variable names have been omitted from the
- prototype in line 4 merely as an illustration that they are
- interpreted as comments by the C++ compiler. The function header
- is formatted differently to allow for a comment alongside each of
- the actual parameters. This should make the function header a
- little more self explanatory. However, you should remember that
- comments should not be used to replace careful selection of
- variable names. In this particular case, the comments add
- essentially nothing to the clarity of the program.
-
-
-
- WHAT DOES PROTOTYPING COST?
- _________________________________________________________________
-
- Prototyping is essentially free because it costs absolutely nothing
- concerning the run time size or speed of execution. Prototyping
- is a compile time check and slows down the compile time a
- negligible amount because of the extra checking that the compiler
- must do. If prototyping finds one error for you that you would
- have had to find with a debugger, it has more than paid for itself
-
- Page 4-3
-
- Chapter 4 - Functions
-
- for use in an entire project. I once spent 12 hours of debugging
- time to find that I forgot to pass the address of a variable to a
- function. Prototyping would have found the error on the first
- compilation of this 2000 line program.
-
- The only price you pay to use prototyping is the extra size of the
- source files because of the prototypes, and the extra time for the
- compiler to read the prototypes during the compilation process, but
- both costs are negligible.
-
- Be sure to compile and execute this example program. You will find
- that it is identical to the last example program.
-
-
- PASS BY REFERENCE
- _________________________________________________________________
-
- Examine The file named PASSREF.CPP for an ===============
- example of a pass by reference, a construct PASSREF.CPP
- which is not available in ANSI-C. The reference ===============
- variable was mentioned in chapter 1 and it was
- recommended there that you don't use it in the
- manner illustrated there. This example program illustrates a
- situation where it can be used to your advantage. The pass by
- reference allows the passing of a variable to a function and
- returning the changes made in the function to the main program.
- In ANSI-C the same effect can be seen when a pointer to a variable
- is passed to a function, but use of a reference variable is a
- little cleaner.
-
- Observe the prototype in line 4 where the second variable has an
- ampersand in front of the variable name. The ampersand instructs
- the compiler to treat this variable just like it were passed a
- pointer to the variable since the actual variable from the main
- program will be used in the function. In the function itself, in
- lines 21 through 24, the variable in2 is used just like any other
- variable but we are using the variable passed to this function from
- the main program not a copy of it. The other variable named in1
- is treated just like any other normal variable in ANSI-C. In
- effect, the name in2 is a synonym for the variable named index in
- the main program.
-
- If you prefer to omit the variable names in the prototypes, you
- would write the prototype as follows;
-
- void fiddle(int, int&);
-
- If you are a Pascal programmer, you will recognize that the
- variable named in1 is treated just like a normal parameter in a
- Pascal call, a call by value. The variable named in2 however, is
- treated like a variable with the reserved word VAR used in front
- of it usually referred to as a call by reference. The reference
- variable is actually a self dereferencing pointer which refers to,
- or points to, the original value.
-
- Page 4-4
-
- Chapter 4 - Functions
-
- When you compile and execute this program, you will find that the
- first variable got changed in the function but was returned to its
- original value when we returned to the main program. The second
- variable however, was changed in the function and the new value was
- reflected back into the variable in the main program which we can
- see when the values are listed on the monitor.
-
-
- DEFAULT PARAMETERS
- _________________________________________________________________
-
- Examine the file named DEFAULT.CPP for an ===============
- example of the use of default parameters in C++. DEFAULT.CPP
- This program really looks strange since it ===============
- contains default values for some of the
- parameters in the prototype, but these default
- values are very useful as we will see shortly.
-
- This prototype says that the first parameter named length must be
- given for each call of this function because a default value is not
- supplied. The second parameter named width, however, is not
- required to be specified for each call, and if it is not specified,
- the value 2 will be used for the variable width within the
- function. Likewise, the third parameter is optional, and if it is
- not specified, the value of 3 will be used for height within the
- function.
-
- In line 11 of this program, all three parameters are specified so
- there is nothing unusual about this call from any other function
- call we have made. Only two values are specified in line 12
- however, so we will use the default value for the third parameter
- and the system acts as if we called it with get_value(x, y, 3)
- since the default value for the third value is 3. In line 13, we
- only specified one parameter which will be used for the first
- formal parameter, and the other two will be defaulted. The system
- will act as if we had called the function with get_volume(x, 2, 3).
- Note that the output from these three lines is reversed. This will
- be explained shortly.
-
- There are a few rules which should be obvious but will be stated
- anyway. Once a parameter is given a default value in the list of
- formal parameters, all of the remaining must have default values
- also. It is not possible to leave a hole in the middle of the
- list, only the trailing values can be defaulted. Of course, the
- defaulted values must be of the correct types or a compiler error
- will be issued. The default values can be given in either the
- prototype or the function header, but not in both. If they are
- given in both places, the compiler must not only use the default
- value, but it must carefully check to see that both values are
- identical. This could further complicate an already very
- complicated problem, that of writing a C++ compiler.
-
- As a matter of style, it is highly recommended that the default
- values be given in the prototype rather than in the function. The
-
- Page 4-5
-
- Chapter 4 - Functions
-
- reason will be obvious when we begin using object oriented
- programming techniques.
-
-
- WHY IS THE OUTPUT SCRAMBLED?
- _________________________________________________________________
-
- When the compiler finds a cout statement, the complete line of code
- is initially scanned from right to left to evaluate any functions,
- then the data is output field by field from left to right.
- Therefore in line 11, get_value() is evaluated with its internal
- output displayed first. Then the fields of the cout are displayed
- from left to right with "Some box data is" displayed next.
- Finally, the result of the return from get_value() is output in int
- format, the type of the returned value. The end result is that the
- output is not in the expected order when lines 11 through 13 are
- executed. (The output is not what you would intuitively expect to
- happen so appears to be a deficiency in the language. A call to
- Borland International, the writers of Turbo C++ and Borland C++,
- verified that this is operating correctly.)
-
- Lines 15 through 18 are similar to any two of the lines of code in
- lines 11 through 13, but are each separated into two lines so the
- output is in the expected order.
-
- Be sure to compile and execute DEFAULT.CPP after you understand it.
- Note that the funny output order will appear again later in this
- tutorial.
-
-
- VARIABLE NUMBER OF ARGUMENTS
- _________________________________________________________________
-
- Examine the program named VARARGS.CPP for an ===============
- illustration of the use of a variable number of VARARGS.CPP
- arguments in a function call. ===============
-
- We have gone to a lot of trouble to get the
- compiler to help us by carefully checking how many parameters we
- use in the function calls and checking the types of the parameters.
- On rare occasion, we may wish to write a function that uses a
- variable number of parameters. The printf() function is a good
- example of this. ANSI-C has a series of three macros available in
- the "stdarg.h" header file to allow the use of a variable number
- of arguments. These are available for use with C++ also, but we
- need a way to eliminate the strong type checking that is done with
- all C++ functions. The three dots illustrated in line 6 will do
- this for us. This prototype says that a single argument of type
- int is required as the first parameter, then no further type
- checking will be done by the compiler.
-
- You will note that the main program consists of three calls to the
- function, each with a different number of parameters, and the
- system does not balk at the differences in the function calls. In
-
- Page 4-6
-
- Chapter 4 - Functions
-
- fact, you could put as many different types as you desire in the
- calls. As long as the first one is an int type variable, the
- system will do its best to compile and run it for you. Of course
- the compiler is ignoring all type checking beyond the first
- parameter so it is up to you to make sure you use the correct
- parameter types in this call.
-
- In this case the first parameter gives the system the number of
- additional parameters to look for and handle. In this simple
- program, we simply display the numbers on the monitor to illustrate
- that they really did get handled properly.
-
- Of course, you realize that using a variable number of arguments
- in a function call can lead to very obscure code and should be used
- very little in a production program, but the capability exists if
- you need it. Be sure to compile and execute this program.
-
-
- FUNCTION NAME OVERLOADING
- _________________________________________________________________
-
- Examine the file named OVERLOAD.CPP for an ================
- example of a program with the function names OVERLOAD.CPP
- overloaded. This is not possible in ANSI-C, but ================
- is perfectly legal and in fact used quite
- regularly in C++. At first this will seem a bit
- strange, but it is one of the keystones of object oriented
- programming. You will see its utility and purpose very clearly in
- later chapters of this tutorial.
-
- You will notice in this example program that there are three
- functions, in addition to the main function, and all three have the
- same name. Your first question is likely to be, "Which function
- do you call when you call do_stuff()?" That is a valid question
- and the answer is, the function that has the correct number of
- formal parameters of the correct types. If do_stuff() is called
- with an integer value or variable as its actual parameter, the
- function beginning in line 23 will be called and executed. If the
- single actual parameter is of type float, the function beginning
- in line 28 will be called, and if two floats are specified, the
- function beginning in line 34 will be called.
-
- It should be noted that the return type is not used to determine
- which function will be called. Only the formal parameters are used
- to determine which overloaded function will be called.
-
-
- The keyword overload used in line 4 tells the system that you
- really do intend to overload the name do_stuff, and the overloading
- is not merely an oversight. This is only required in C++ version
- 1.2. C++ version 2.0 and greater do not require the keyword
- overload but allows it to be used optionally in order to allow the
- existing body of C++ code to be compatible with newer compilers.
- It is not necessary to use this keyword because, when overloading
-
- Page 4-7
-
- Chapter 4 - Functions
-
- is used in C++, it is generally used in a context in which it is
- obvious that the function name is overloaded.
-
- The actual selection of which function to actually call is done at
- compile time, not at execution time so the program is not slowed
- down. If each of the overloaded function names were changed to
- different names, each being unique, there would be no difference
- in execution size or speed of the resulting program.
-
- Overloading of function names may seem very strange to you, and it
- is strange if you are used to the rules of K&R or ANSI-C
- programming. As you gain experience with C++, you will feel very
- comfortable with this and you will use it a lot in your C++
- programming.
-
- Note the use of the keyword const used in some of the function
- prototypes and headers. Once again, this prevents the programmer
- from accidentally changing the formal parameter within the
- function. In a function as short as these, there is no real
- problem with an accidental assignment. In a real function that you
- occasionally modify, you could easily forget the original intention
- of the use of a value and attempt to change it during an extended
- debugging session.
-
-
- PROGRAMMING EXERCISES
- _________________________________________________________________
-
-
- 1. Change the type of wings in the prototype of PROTYPE1.CPP to
- float so that it disagrees with the function definition to see
- if you get a compilation error.
-
- 2. Change the function definition in PROTYPE1.CPP to agree with
- the changed prototype. Compile and execute the program
- without changing the calls in lines 12 and 13. Explain the
- results.
-
- 3. In DEFAULT.CPP, remove the default value from the prototype
- for height only to see what kind of compiler error you get.
- Only the last values of the list can be defaulted.
-
- 4. In OVERLOAD.CPP, change the names of the three functions so
- that each is a unique name and compare the size of the
- resulting executable file with that given for the present
- program.
-
-
-
-
-
-
-
-
-
- Page 4-8
-